"""
Feature Flag模块 - 动态功能开关

这个模块实现了动态功能开关功能：
- 功能开关管理
- 动态配置更新
- 用户和分组定向
- A/B测试支持
- 开关状态监控
"""

import asyncio
import logging
import time
import json
from dataclasses import dataclass, field
from datetime import datetime, timezone, timedelta
from enum import Enum
from typing import Any, Dict, List, Optional, Union, Callable
import hashlib
import random

logger = logging.getLogger(__name__)


class FeatureFlagType(Enum):
    """功能开关类型"""
    BOOLEAN = "boolean"        # 布尔开关
    STRING = "string"          # 字符串值
    NUMBER = "number"          # 数值
    JSON = "json"              # JSON对象
    PERCENTAGE = "percentage"  # 百分比


class FeatureFlagStatus(Enum):
    """功能开关状态"""
    ENABLED = "enabled"        # 启用
    DISABLED = "disabled"      # 禁用
    ROLLOUT = "rollout"        # 逐步推出
    EXPERIMENT = "experiment"  # 实验


class TargetingRule(Enum):
    """定向规则类型"""
    ALL = "all"                # 所有用户
    USER_ID = "user_id"        # 用户ID
    USER_GROUP = "user_group"  # 用户组
    IP_ADDRESS = "ip_address"  # IP地址
    COUNTRY = "country"        # 国家
    DEVICE = "device"          # 设备类型
    CUSTOM = "custom"          # 自定义规则


@dataclass
class FeatureFlagConfig:
    """功能开关配置"""
    # 基本配置
    name: str
    description: str = ""
    flag_type: FeatureFlagType = FeatureFlagType.BOOLEAN
    status: FeatureFlagStatus = FeatureFlagStatus.DISABLED
    
    # 默认值
    default_value: Any = False
    enabled_value: Any = True
    disabled_value: Any = False
    
    # 定向配置
    targeting_rules: List[Dict[str, Any]] = field(default_factory=list)
    
    # 逐步推出配置
    rollout_percentage: float = 0.0  # 0-100
    rollout_groups: List[str] = field(default_factory=list)
    
    # 实验配置
    experiment_config: Optional[Dict[str, Any]] = None
    
    # 元数据
    tags: List[str] = field(default_factory=list)
    created_by: str = "system"
    created_at: datetime = field(default_factory=lambda: datetime.now(timezone.utc))
    updated_at: datetime = field(default_factory=lambda: datetime.now(timezone.utc))
    
    # 过期配置
    expires_at: Optional[datetime] = None
    
    # 监控配置
    enable_analytics: bool = True
    enable_audit_log: bool = True


@dataclass
class FeatureFlagEvaluation:
    """功能开关评估结果"""
    flag_name: str
    value: Any
    reason: str
    variant: Optional[str] = None
    targeting_rule: Optional[str] = None
    evaluated_at: datetime = field(default_factory=lambda: datetime.now(timezone.utc))


@dataclass
class FeatureFlagAnalytics:
    """功能开关分析数据"""
    flag_name: str
    total_evaluations: int = 0
    enabled_evaluations: int = 0
    disabled_evaluations: int = 0
    variant_evaluations: Dict[str, int] = field(default_factory=dict)
    last_evaluated: Optional[datetime] = None


class FeatureFlagManager:
    """功能开关管理器"""
    
    def __init__(self):
        self.flags: Dict[str, FeatureFlagConfig] = {}
        self.analytics: Dict[str, FeatureFlagAnalytics] = {}
        self.audit_log: List[Dict[str, Any]] = []
        
        # 回调函数
        self.change_callbacks: List[Callable] = []
        self.evaluation_callbacks: List[Callable] = []
        
        # 锁
        self.lock = asyncio.Lock()
        
        self.logger = logging.getLogger(__name__)
        self.logger.info("功能开关管理器已初始化")
    
    async def create_flag(self, config: FeatureFlagConfig) -> bool:
        """创建功能开关"""
        async with self.lock:
            if config.name in self.flags:
                self.logger.warning(f"功能开关已存在: {config.name}")
                return False
            
            self.flags[config.name] = config
            self.analytics[config.name] = FeatureFlagAnalytics(flag_name=config.name)
            
            # 记录审计日志
            await self._log_audit("CREATE", config.name, {
                "description": config.description,
                "type": config.flag_type.value,
                "status": config.status.value,
                "created_by": config.created_by
            })
            
            self.logger.info(f"功能开关创建成功: {config.name}")
            return True
    
    async def update_flag(self, 
                         flag_name: str,
                         updates: Dict[str, Any],
                         updated_by: str = "system") -> bool:
        """更新功能开关"""
        async with self.lock:
            if flag_name not in self.flags:
                self.logger.warning(f"功能开关不存在: {flag_name}")
                return False
            
            old_config = self.flags[flag_name]
            
            # 更新配置
            for key, value in updates.items():
                if hasattr(old_config, key):
                    setattr(old_config, key, value)
            
            old_config.updated_at = datetime.now(timezone.utc)
            
            # 记录审计日志
            await self._log_audit("UPDATE", flag_name, {
                "updates": updates,
                "updated_by": updated_by
            })
            
            # 触发变更回调
            await self._trigger_change_callbacks(flag_name, old_config)
            
            self.logger.info(f"功能开关更新成功: {flag_name}")
            return True
    
    async def delete_flag(self, flag_name: str, deleted_by: str = "system") -> bool:
        """删除功能开关"""
        async with self.lock:
            if flag_name not in self.flags:
                self.logger.warning(f"功能开关不存在: {flag_name}")
                return False
            
            del self.flags[flag_name]
            if flag_name in self.analytics:
                del self.analytics[flag_name]
            
            # 记录审计日志
            await self._log_audit("DELETE", flag_name, {
                "deleted_by": deleted_by
            })
            
            self.logger.info(f"功能开关删除成功: {flag_name}")
            return True
    
    async def evaluate_flag(self, 
                           flag_name: str,
                           context: Dict[str, Any]) -> FeatureFlagEvaluation:
        """评估功能开关"""
        async with self.lock:
            if flag_name not in self.flags:
                return FeatureFlagEvaluation(
                    flag_name=flag_name,
                    value=self._get_default_value(flag_name),
                    reason="Flag not found"
                )
            
            config = self.flags[flag_name]
            
            # 检查是否过期
            if config.expires_at and datetime.now(timezone.utc) > config.expires_at:
                return FeatureFlagEvaluation(
                    flag_name=flag_name,
                    value=config.disabled_value,
                    reason="Flag expired"
                )
            
            # 评估功能开关
            evaluation = await self._evaluate_flag_config(config, context)
            
            # 更新分析数据
            if config.enable_analytics:
                await self._update_analytics(flag_name, evaluation)
            
            # 触发评估回调
            await self._trigger_evaluation_callbacks(flag_name, evaluation, context)
            
            return evaluation
    
    async def _evaluate_flag_config(self, 
                                   config: FeatureFlagConfig,
                                   context: Dict[str, Any]) -> FeatureFlagEvaluation:
        """评估功能开关配置"""
        # 检查状态
        if config.status == FeatureFlagStatus.DISABLED:
            return FeatureFlagEvaluation(
                flag_name=config.name,
                value=config.disabled_value,
                reason="Flag disabled"
            )
        
        elif config.status == FeatureFlagStatus.ENABLED:
            # 检查定向规则
            targeting_result = await self._evaluate_targeting(config, context)
            if targeting_result["matched"]:
                return FeatureFlagEvaluation(
                    flag_name=config.name,
                    value=config.enabled_value,
                    reason="Flag enabled",
                    targeting_rule=targeting_result.get("rule_name")
                )
            else:
                return FeatureFlagEvaluation(
                    flag_name=config.name,
                    value=config.disabled_value,
                    reason="Targeting rule not matched"
                )
        
        elif config.status == FeatureFlagStatus.ROLLOUT:
            # 逐步推出逻辑
            return await self._evaluate_rollout(config, context)
        
        elif config.status == FeatureFlagStatus.EXPERIMENT:
            # 实验逻辑
            return await self._evaluate_experiment(config, context)
        
        else:
            return FeatureFlagEvaluation(
                flag_name=config.name,
                value=config.disabled_value,
                reason="Unknown status"
            )
    
    async def _evaluate_targeting(self, 
                                 config: FeatureFlagConfig,
                                 context: Dict[str, Any]) -> Dict[str, Any]:
        """评估定向规则"""
        if not config.targeting_rules:
            return {"matched": True, "rule_name": "default"}
        
        for rule in config.targeting_rules:
            rule_type = rule.get("type")
            rule_config = rule.get("config", {})
            
            if rule_type == TargetingRule.ALL.value:
                return {"matched": True, "rule_name": rule.get("name", "all")}
            
            elif rule_type == TargetingRule.USER_ID.value:
                user_ids = rule_config.get("user_ids", [])
                user_id = context.get("user_id")
                if user_id and user_id in user_ids:
                    return {"matched": True, "rule_name": rule.get("name", "user_id")}
            
            elif rule_type == TargetingRule.USER_GROUP.value:
                groups = rule_config.get("groups", [])
                user_group = context.get("user_group")
                if user_group and user_group in groups:
                    return {"matched": True, "rule_name": rule.get("name", "user_group")}
            
            elif rule_type == TargetingRule.IP_ADDRESS.value:
                ip_ranges = rule_config.get("ip_ranges", [])
                ip_address = context.get("ip_address")
                if ip_address and self._is_ip_in_ranges(ip_address, ip_ranges):
                    return {"matched": True, "rule_name": rule.get("name", "ip_address")}
            
            elif rule_type == TargetingRule.COUNTRY.value:
                countries = rule_config.get("countries", [])
                country = context.get("country")
                if country and country in countries:
                    return {"matched": True, "rule_name": rule.get("name", "country")}
            
            elif rule_type == TargetingRule.DEVICE.value:
                devices = rule_config.get("devices", [])
                device = context.get("device")
                if device and device in devices:
                    return {"matched": True, "rule_name": rule.get("name", "device")}
            
            elif rule_type == TargetingRule.CUSTOM.value:
                custom_func = rule_config.get("function")
                if custom_func and callable(custom_func):
                    if custom_func(context):
                        return {"matched": True, "rule_name": rule.get("name", "custom")}
        
        return {"matched": False}
    
    async def _evaluate_rollout(self, 
                               config: FeatureFlagConfig,
                               context: Dict[str, Any]) -> FeatureFlagEvaluation:
        """评估逐步推出"""
        # 检查用户是否在推出组中
        user_id = context.get("user_id")
        if user_id and config.rollout_groups:
            user_group = context.get("user_group")
            if user_group and user_group in config.rollout_groups:
                return FeatureFlagEvaluation(
                    flag_name=config.name,
                    value=config.enabled_value,
                    reason="User in rollout group"
                )
        
        # 基于百分比的推出
        if config.rollout_percentage > 0:
            user_id = context.get("user_id", "anonymous")
            hash_value = self._hash_user_id(user_id, config.name)
            percentage = (hash_value % 100) + 1
            
            if percentage <= config.rollout_percentage:
                return FeatureFlagEvaluation(
                    flag_name=config.name,
                    value=config.enabled_value,
                    reason=f"Rollout percentage: {percentage}%"
                )
        
        return FeatureFlagEvaluation(
            flag_name=config.name,
            value=config.disabled_value,
            reason="Not in rollout"
        )
    
    async def _evaluate_experiment(self, 
                                  config: FeatureFlagConfig,
                                  context: Dict[str, Any]) -> FeatureFlagEvaluation:
        """评估实验"""
        if not config.experiment_config:
            return FeatureFlagEvaluation(
                flag_name=config.name,
                value=config.disabled_value,
                reason="No experiment config"
            )
        
        experiment_config = config.experiment_config
        variants = experiment_config.get("variants", [])
        
        if not variants:
            return FeatureFlagEvaluation(
                flag_name=config.name,
                value=config.enabled_value,
                reason="Experiment enabled"
            )
        
        # 基于用户ID分配变体
        user_id = context.get("user_id", "anonymous")
        hash_value = self._hash_user_id(user_id, config.name)
        variant_index = hash_value % len(variants)
        variant = variants[variant_index]
        
        return FeatureFlagEvaluation(
            flag_name=config.name,
            value=variant.get("value", config.enabled_value),
            reason="Experiment variant",
            variant=variant.get("name", f"variant_{variant_index}")
        )
    
    def _hash_user_id(self, user_id: str, flag_name: str) -> int:
        """哈希用户ID"""
        hash_input = f"{user_id}:{flag_name}"
        hash_value = hashlib.md5(hash_input.encode()).hexdigest()
        return int(hash_value[:8], 16)
    
    def _is_ip_in_ranges(self, ip_address: str, ip_ranges: List[str]) -> bool:
        """检查IP是否在范围内"""
        # 简化的IP范围检查实现
        for ip_range in ip_ranges:
            if ip_range == ip_address:
                return True
            if "/" in ip_range:
                # CIDR范围检查（简化实现）
                if self._is_ip_in_cidr(ip_address, ip_range):
                    return True
        return False
    
    def _is_ip_in_cidr(self, ip_address: str, cidr: str) -> bool:
        """检查IP是否在CIDR范围内（简化实现）"""
        # 这里应该实现完整的CIDR检查逻辑
        # 简化实现，只检查前缀匹配
        network, prefix = cidr.split("/")
        prefix_len = int(prefix)
        
        # 简化的前缀匹配
        ip_parts = ip_address.split(".")
        network_parts = network.split(".")
        
        for i in range(prefix_len // 8):
            if ip_parts[i] != network_parts[i]:
                return False
        
        return True
    
    def _get_default_value(self, flag_name: str) -> Any:
        """获取默认值"""
        # 这里可以根据配置返回默认值
        return False
    
    async def _update_analytics(self, 
                               flag_name: str,
                               evaluation: FeatureFlagEvaluation) -> None:
        """更新分析数据"""
        if flag_name not in self.analytics:
            self.analytics[flag_name] = FeatureFlagAnalytics(flag_name=flag_name)
        
        analytics = self.analytics[flag_name]
        analytics.total_evaluations += 1
        analytics.last_evaluated = evaluation.evaluated_at
        
        # 检查是否启用
        if evaluation.value == self.flags[flag_name].enabled_value:
            analytics.enabled_evaluations += 1
        else:
            analytics.disabled_evaluations += 1
        
        # 记录变体
        if evaluation.variant:
            analytics.variant_evaluations[evaluation.variant] = \
                analytics.variant_evaluations.get(evaluation.variant, 0) + 1
    
    async def _log_audit(self, 
                        action: str,
                        flag_name: str,
                        details: Dict[str, Any]) -> None:
        """记录审计日志"""
        audit_entry = {
            "timestamp": datetime.now(timezone.utc).isoformat(),
            "action": action,
            "flag_name": flag_name,
            "details": details
        }
        
        self.audit_log.append(audit_entry)
        
        # 限制审计日志大小
        if len(self.audit_log) > 10000:
            self.audit_log = self.audit_log[-5000:]
    
    async def _trigger_change_callbacks(self, 
                                       flag_name: str,
                                       config: FeatureFlagConfig) -> None:
        """触发变更回调"""
        for callback in self.change_callbacks:
            try:
                if asyncio.iscoroutinefunction(callback):
                    await callback(flag_name, config)
                else:
                    callback(flag_name, config)
            except Exception as e:
                self.logger.error(f"变更回调执行失败: {e}")
    
    async def _trigger_evaluation_callbacks(self, 
                                           flag_name: str,
                                           evaluation: FeatureFlagEvaluation,
                                           context: Dict[str, Any]) -> None:
        """触发评估回调"""
        for callback in self.evaluation_callbacks:
            try:
                if asyncio.iscoroutinefunction(callback):
                    await callback(flag_name, evaluation, context)
                else:
                    callback(flag_name, evaluation, context)
            except Exception as e:
                self.logger.error(f"评估回调执行失败: {e}")
    
    async def get_flag(self, flag_name: str) -> Optional[FeatureFlagConfig]:
        """获取功能开关"""
        return self.flags.get(flag_name)
    
    async def list_flags(self, 
                        status: Optional[FeatureFlagStatus] = None,
                        tags: Optional[List[str]] = None) -> List[FeatureFlagConfig]:
        """列出功能开关"""
        flags = list(self.flags.values())
        
        if status:
            flags = [f for f in flags if f.status == status]
        
        if tags:
            flags = [f for f in flags if any(tag in f.tags for tag in tags)]
        
        return flags
    
    async def get_analytics(self, flag_name: Optional[str] = None) -> Dict[str, Any]:
        """获取分析数据"""
        if flag_name:
            return self.analytics.get(flag_name, FeatureFlagAnalytics(flag_name=flag_name)).__dict__
        else:
            return {name: analytics.__dict__ for name, analytics in self.analytics.items()}
    
    async def get_audit_log(self, 
                           flag_name: Optional[str] = None,
                           limit: int = 100) -> List[Dict[str, Any]]:
        """获取审计日志"""
        log_entries = self.audit_log
        
        if flag_name:
            log_entries = [entry for entry in log_entries if entry["flag_name"] == flag_name]
        
        return log_entries[-limit:]
    
    def add_change_callback(self, callback: Callable) -> None:
        """添加变更回调"""
        self.change_callbacks.append(callback)
    
    def add_evaluation_callback(self, callback: Callable) -> None:
        """添加评估回调"""
        self.evaluation_callbacks.append(callback)
    
    async def export_flags(self) -> Dict[str, Any]:
        """导出功能开关配置"""
        return {
            "flags": {
                name: {
                    "name": config.name,
                    "description": config.description,
                    "type": config.flag_type.value,
                    "status": config.status.value,
                    "default_value": config.default_value,
                    "enabled_value": config.enabled_value,
                    "disabled_value": config.disabled_value,
                    "targeting_rules": config.targeting_rules,
                    "rollout_percentage": config.rollout_percentage,
                    "rollout_groups": config.rollout_groups,
                    "experiment_config": config.experiment_config,
                    "tags": config.tags,
                    "created_by": config.created_by,
                    "created_at": config.created_at.isoformat(),
                    "updated_at": config.updated_at.isoformat(),
                    "expires_at": config.expires_at.isoformat() if config.expires_at else None,
                    "enable_analytics": config.enable_analytics,
                    "enable_audit_log": config.enable_audit_log
                }
                for name, config in self.flags.items()
            },
            "exported_at": datetime.now(timezone.utc).isoformat()
        }
    
    async def import_flags(self, data: Dict[str, Any]) -> int:
        """导入功能开关配置"""
        imported_count = 0
        
        for flag_name, flag_data in data.get("flags", {}).items():
            try:
                # 创建配置对象
                config = FeatureFlagConfig(
                    name=flag_data["name"],
                    description=flag_data.get("description", ""),
                    flag_type=FeatureFlagType(flag_data["type"]),
                    status=FeatureFlagStatus(flag_data["status"]),
                    default_value=flag_data.get("default_value", False),
                    enabled_value=flag_data.get("enabled_value", True),
                    disabled_value=flag_data.get("disabled_value", False),
                    targeting_rules=flag_data.get("targeting_rules", []),
                    rollout_percentage=flag_data.get("rollout_percentage", 0.0),
                    rollout_groups=flag_data.get("rollout_groups", []),
                    experiment_config=flag_data.get("experiment_config"),
                    tags=flag_data.get("tags", []),
                    created_by=flag_data.get("created_by", "import"),
                    created_at=datetime.fromisoformat(flag_data.get("created_at", datetime.now(timezone.utc).isoformat())),
                    updated_at=datetime.fromisoformat(flag_data.get("updated_at", datetime.now(timezone.utc).isoformat())),
                    expires_at=datetime.fromisoformat(flag_data["expires_at"]) if flag_data.get("expires_at") else None,
                    enable_analytics=flag_data.get("enable_analytics", True),
                    enable_audit_log=flag_data.get("enable_audit_log", True)
                )
                
                # 创建功能开关
                if await self.create_flag(config):
                    imported_count += 1
                
            except Exception as e:
                self.logger.error(f"导入功能开关失败: {flag_name}, 错误: {e}")
        
        self.logger.info(f"导入功能开关完成: {imported_count} 个")
        return imported_count


# 全局功能开关管理器
_global_feature_flag_manager: Optional[FeatureFlagManager] = None


def get_feature_flag_manager() -> FeatureFlagManager:
    """获取全局功能开关管理器"""
    global _global_feature_flag_manager
    if _global_feature_flag_manager is None:
        _global_feature_flag_manager = FeatureFlagManager()
    return _global_feature_flag_manager


# 装饰器
def feature_flag(flag_name: str, 
                 default_value: Any = False,
                 context_provider: Optional[Callable] = None):
    """功能开关装饰器"""
    def decorator(func):
        async def wrapper(*args, **kwargs):
            manager = get_feature_flag_manager()
            
            # 获取上下文
            context = {}
            if context_provider:
                if asyncio.iscoroutinefunction(context_provider):
                    context = await context_provider(*args, **kwargs)
                else:
                    context = context_provider(*args, **kwargs)
            
            # 评估功能开关
            evaluation = await manager.evaluate_flag(flag_name, context)
            
            if evaluation.value:
                return await func(*args, **kwargs)
            else:
                return default_value
        
        return wrapper
    return decorator
